package config_center

import (
	"context"
	"payme3000/framework/errorx"
	frameworkRecover "payme3000/framework/recover"
)

// ConfigItem 配置项
type ConfigItem struct {
	Key        string
	Value      IAtomic
	NotifyFunc func(IAtomic)
}

// Notify 触发变更回调
func (l *ConfigItem) Notify() {
	if l.NotifyFunc != nil {
		l.NotifyFunc(l.Value)
	}
}

// Watch 观察配置项, 配置中心必须已有该配置项
func (c *ConfigCenter) Watch(configItem *ConfigItem) error {
	c.configItemLock.Lock()
	defer c.configItemLock.Unlock()

	// 锁定后再检查一次
	if _configItem := c.configItemMap[configItem.Key]; _configItem != nil {
		return nil
	}

	c.configItemMap[configItem.Key] = configItem

	// 从资料库查询该key的配置值
	dbValue, err := c.Get(configItem.Key)
	if err != nil {
		return err
	} else if dbValue == nil {
		return errorx.Errorf(context.Background(), "watcher value not found with key:%s", configItem.Key)
	}

	// 更新
	configItem.Value.Store(*dbValue)

	// 通知
	configItem.Notify()

	// 开始监控
	c.configItemOnce.Do(func() {
		go frameworkRecover.WithRecover(c.monitor)
	})

	return nil
}

// Listen 监听配置项, 配置中心不必有该配置项
func (c *ConfigCenter) Listen(configItem *ConfigItem) error {
	c.configItemLock.Lock()
	defer c.configItemLock.Unlock()

	// 锁定后再检查一次
	if _configItem := c.configItemMap[configItem.Key]; _configItem != nil {
		return nil
	}

	c.configItemMap[configItem.Key] = configItem

	// 从资料库查询该key的配置值
	dbValue, err := c.Get(configItem.Key)
	if err != nil {
		return err
	} else if dbValue != nil {
		// 更新
		configItem.Value.Store(*dbValue)

		// 通知
		configItem.Notify()
	}

	// 开始监控
	c.configItemOnce.Do(func() {
		go frameworkRecover.WithRecover(c.monitor)
	})

	return nil
}

// BatchListen 批量监听配置项
func (c *ConfigCenter) BatchListen(configItemList ...*ConfigItem) error {
	c.configItemLock.Lock()
	defer c.configItemLock.Unlock()

	for _, configItem := range configItemList {
		// 锁定后再检查一次
		if _configItem := c.configItemMap[configItem.Key]; _configItem != nil {
			continue
		}

		c.configItemMap[configItem.Key] = configItem

		// 从资料库查询该key的配置值
		dbValue, err := c.Get(configItem.Key)
		if err != nil {
			return err
		} else if dbValue != nil {
			// 更新
			configItem.Value.Store(*dbValue)

			// 通知
			configItem.Notify()
		}
	}

	// 开始监控
	c.configItemOnce.Do(func() {
		go frameworkRecover.WithRecover(c.monitor)
	})

	return nil
}
